﻿#include "connect_pool.hpp"
#include "public.hpp"

#define TEMP_SIZE 1024

Connect_pool *Connect_pool::get_instance()
{
    static Connect_pool pool;
    return &pool;
}

//给外部提供的接口，返回可用的空闲连接
std::shared_ptr<Connect> Connect_pool::get_connect()
{
    std::unique_lock<std::mutex> lock(queue_mutex_);
    while (connect_queue_.empty())
    {
        if (std::cv_status::timeout == condition_.wait_for(lock, std::chrono::milliseconds(connection_timeout_)))
        {
            if (connect_queue_.empty())
            {
                LOG("get connection timeout ...");
                return nullptr;
            }
        }
    }

    /*
    * 需要重定义 shared_ptr 删除器
    */
    std::shared_ptr<Connect> ret(connect_queue_.front(), [&](Connect *ptr) {
        std::unique_lock<std::mutex> lock(queue_mutex_);
        ptr->refresh_aliveTime(); //刷新以下开始空闲的起始时间
        connect_queue_.push(ptr);
    });
    connect_queue_.pop();
    if (connect_queue_.empty())
    {
        //没有可用连接了，通知生产线程去生产
        condition_.notify_all();
    }

    return ret;
}

Connect_pool::Connect_pool()
{
    //加载配置项
    if (!is_init_)
    {
        LOG("configure file error!");
        return;
    }

    //创建初始数量连接
    for (int i = 0; i < init_size_; i++)
    {
        Connect *p = new Connect();
        if (p->connect(ip_, port_, user_, password_, dbname_)== false)
        {
            std::cout << "connect error!" << std::endl;
        }
        p->refresh_aliveTime(); //刷新以下开始空闲的起始时间
        connect_queue_.push(p);
        connect_cnt_++;
    }

    //启动一个新线程，作为连接生产者
    std::thread connect_producer(std::bind(&Connect_pool::produce_connection, this));
    connect_producer.detach();

    //启动新线程扫描空闲连接，超过max freetime时间的空闲连接，进行连接回收
    std::thread scaner(std::bind(&Connect_pool::scan_connection, this));
    scaner.detach();
}

std::string Connect_pool::ip_;
unsigned short Connect_pool::port_;
std::string Connect_pool::user_;
std::string Connect_pool::password_;
std::string Connect_pool::dbname_;
int Connect_pool::init_size_;         
int Connect_pool::max_size_;          
int Connect_pool::max_freeTime_;      
int Connect_pool::connection_timeout_;
bool Connect_pool::is_init_;

//加载配置文件
bool Connect_pool::set_configure(
    std::string ip,
    unsigned short port,
    std::string user,
    std::string password,
    std::string dbname,
    int init_size,          //连接池初始连接量
    int max_size,           //连接的最大连接量
    int max_freeTime,        //连接池的最大空闲时间(s)
    int connection_timeout //连接池获取连接的超时时间(ms)
){
    ip_ = ip;
    port_= port;
    user_= user;
    password_= password;
    dbname_= dbname;
    init_size_= init_size;          //连接池初始连接量
    max_size_= max_size;           //连接的最大连接量
    max_freeTime_= max_freeTime;        //连接池的最大空闲时间(s)
    connection_timeout_= connection_timeout; //连接池获取连接的超时时间(ms)
    is_init_ = true;
    return is_init_;
}

// 运行在独立的线程，专门负责生产新的连接
void Connect_pool::produce_connection()
{
    for (;;)
    {
        std::unique_lock<std::mutex> lock(queue_mutex_);
        while (!connect_queue_.empty())
        {
            condition_.wait(lock);
        }

        //还没到最大值，可创建
        if (connect_cnt_ < max_size_)
        {
            Connect *p = new Connect();
            p->connect(ip_, port_, user_, password_, dbname_);
            p->refresh_aliveTime(); //刷新以下开始空闲的起始时间
            connect_queue_.push(p);
            connect_cnt_++;
        }

        //通知消费者线程可消费了
        condition_.notify_all();
    }
}

//释放空闲连接
void Connect_pool::scan_connection()
{
    for (;;)
    {
        //通过sleep模型定时效果
        std::this_thread::sleep_for(std::chrono::seconds(max_freeTime_));

        //扫描队列，释放多余连接
        std::unique_lock<std::mutex> lock(queue_mutex_);
        while (connect_cnt_ > init_size_)
        {
            Connect *p = connect_queue_.front();
            if (p->get_aliveTime() > max_freeTime_ * 1000)
            {
                connect_queue_.pop();
                delete p; //释放连接
                connect_cnt_--;
            }
        }
    }
}
